﻿using System;
using System.Collections.Generic;
using System.Text;

namespace MiniGert.Transformation
{
  /// <summary>
  /// Represents a single node in the scene graph tree.
  /// </summary>
  /// <typeparam name="T">The type of the data object that is attached to the node.</typeparam>
  public class SceneNode<T>
  {
    /// <summary>
    /// The internal field for the parent node. Null means no parent.
    /// </summary>
    protected SceneNode<T> _parent = null;

    /// <summary>
    /// The internal list of child nodes.
    /// </summary>
    protected List<SceneNode<T>> _children = new List<SceneNode<T>>();

    /// <summary>
    /// The the internal field for the data attached to this node.
    /// </summary>
    protected T _data = default(T);

    /// <summary>
    /// The internal transformation field.
    /// </summary>
    protected Matrix _transform = new Matrix();

    /// <summary>
    /// Creates a new scene node with no parent, no children, no data and no tranformation.
    /// </summary>
    public SceneNode()
    {
    }

    /// <summary>
    /// Creates a new scene node with the given parent, no children, no data and no tranformation.
    /// </summary>
    /// <param name="parent">The parent node of the new node.</param>
    public SceneNode(SceneNode<T> parent)
    {
      _parent = parent;
    }

    /// <summary>
    /// Creates a new scene node with the given data no parent, no children and no tranformation.
    /// </summary>
    /// <param name="data">The data attached to the new node.</param>
    public SceneNode(T data)
    {
      _data = data;
    }

    /// <summary>
    /// Creates a new scene node with the given parent and data, no children and no tranformation.
    /// </summary>
    /// <param name="parent">The parent node of the new node.</param>
    /// <param name="data">The data attached to the new node.</param>
    public SceneNode(SceneNode<T> parent, T data)
    {
      _data = data;
      _parent = parent;
    }

    /// <summary>
    /// Gets or sets the parent of the scene node. If a non-null parent is set, the node is automatically
    /// removed from it's former parents children and added to the new parents children.
    /// </summary>
    public SceneNode<T> Parent
    {
      get { return _parent; }
      set 
      {
        if (value != null)
        {
          if (_parent != null)
            _parent.Children.Remove(this);
          value.Children.Add(this);
        }
        _parent = value; 
      }
    }

    /// <summary>
    /// Gets or sets the list of children of this node.
    /// </summary>
    public List<SceneNode<T>> Children
    {
      get { return _children; }
      set { _children = value; }
    }

    /// <summary>
    /// Gets or sets the trasnformation of this node.
    /// </summary>
    public Matrix Transform
    {
      get { return _transform; }
      set { _transform = value; }
    }

    /// <summary>
    /// Gets if the node is a leaf node (= no children).
    /// </summary>
    public bool IsLeaf
    {
      get { return _children.Count == 0; }
    }

    /// <summary>
    /// Gets if the node is an orphan (= no parent)
    /// </summary>
    public bool IsOrphan
    {
      get { return _parent == null; }
    }

    /// <summary>
    /// Adds a new child to this node and returns it.
    /// </summary>
    /// <returns>The new child.</returns>
    public SceneNode<T> AddChild()
    {
      SceneNode<T> result = new SceneNode<T>();
      _children.Add(result);
      return result;
    }

    /// <summary>
    /// Calculates the tree level of this node.
    /// </summary>
    /// <returns>The tree level of this node.</returns>
    public int GetLevel()
    {
      int result = 0;
      SceneNode<T> next = this;
      while (next != null)
      {
        result++;
        next = next.Parent;
      }
      return result;
    }

    /// <summary>
    /// Checks if the given node is a child node of this node.
    /// </summary>
    /// <param name="node">The node to be checked.</param>
    /// <returns>True if the given node is a child node of this node, else False.</returns>
    public bool IsChild(SceneNode<T> node)
    {
      SceneNode<T> next = node;
      while (next != null)
      {
        if (next == this)
          return true;
        next = next.Parent;
      }
      return false;
    }

  }
}
